package Downloader;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.FileNameMap;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.regex.PatternSyntaxException;
import DataBase.Ficheiro;

public class Downloader {

    private String fullWebPath;
    private String localPath;
    private long size;
    private URL url;
    private String tipo;
    private String host;
    private String fileName;
    private String path;
    private boolean windows;
    private int retornoPagina;
    private String protocol;
    private boolean nivelMax;
    private int nivel;
    private Ficheiro fxVario = new Ficheiro();

    public int fileDownload(String url, String localPath, int nivelMax) throws MalformedURLException {
        try {
            this.url = new URL(url);
        } catch (MalformedURLException ex) {
            System.out.println("\n URL inválido, verificar se protocol existe (http / ftp.....");
//         *  -1 link não estar bem formatado ou bem definido
            return -1;
        }
        this.retornoPagina = 400;
        try {

            this.retornoPagina = Downloader.getResponseCode(url);
        } catch (IOException ex) {
            System.out.println("\n Erro ao obter responseCode de " + ex.getMessage());
            //         *  -2 erro no get response code
            return -2;

        }

        if (this.retornoPagina < 400) {
            this.setFullWebPath(url);
            this.levelVerification(nivelMax);
            if (this.nivelMax) {
                this.URLVerifcation();
                this.setHost();
                this.setLocalPath(localPath);
                this.verifyLastCharacter();
                String tmp = this.getLocalPath();
                tmp += this.getHost();
                if (this.createDirectory(tmp) > 0) {
                    this.setSize();
                    this.setProtocol();
                    this.ifOnlyHost();
                    this.fileType();
                    this.setWindows();
                    this.path = this.convertSlash(this.path);
                } else {
                    //-5 no caso de não conseguir gravar localmente
                    return -5;
                }
            } else {
                //         *  -3 no caso do nivelMax ser superior ao definido
                return -3;
            }
        } else {
            //  *  -4 no caso do ficheiro não existir, erro 400/500
            return -4;
        }
        //*  0 tudo ok
        return 0;
    }

    private int fileDownload() throws MalformedURLException, IOException {
        BufferedInputStream in = null;
        FileOutputStream fout = null;
        try {
            try {
                if (new File(this.getFullLocalPath()).isDirectory()) {
                    this.createDirectory(this.getFullLocalPath());
                }
                in = new BufferedInputStream(new URL(this.getFullWebPath()).openStream());
            } catch (IOException ex) {
                return -1;
            }
            try {
                String tmp = this.getFullLocalPathFile();
                this.createDirectory(this.getFullLocalPath());
                fout = new FileOutputStream(tmp);
            } catch (IOException ex) {
                System.out.println("\nErro não foi possivel criar o ficheiro/directoria local " + ex.getMessage());
                return -2;
            }
            byte data[] = new byte[1024];
            int count;
            while ((count = in.read(data, 0, 1024)) != -1) {
                fout.write(data, 0, count);
            }
        } finally {
            if (in != null) {
                in.close();
            }
            if (fout != null) {
                fout.close();
            }
        }
        this.localFileLength();
        return 1;
    }

    /*
     * resultado -1 - falha ao obter ficheiro externo
     * resultado -2 - falha ao criar ficheiro local
     * resultado  1 - operação ok
     */
    public int downloadFile() {
        int resultado = 1;
        try {
            resultado = this.fileDownload();
        } catch (MalformedURLException e) {
            System.out.println("\nfalha ao obter ficheiro externo " + e.getMessage());
            return -1;
        } catch (IOException ex) {
            System.out.println("\nfalha ao obter ficheiro externo " + ex.getMessage());
        }
        return resultado;
    }

    private String testFileName(String fileName) {
        int idx = fileName.replaceAll("\\\\", "/").lastIndexOf("/");
        idx++;
        return idx >= 0 ? fileName.substring(idx) : fileName;
    }

    /*
     * Este metodo verifica o contentType do ficheiro a descarregar
     */
    private boolean fileType() {
        FileNameMap fileNameMap = URLConnection.getFileNameMap();

        try {
            String mimeType = fileNameMap.getContentTypeFor(this.fullWebPath);
            if (mimeType.equals("text/html")) {

                this.tipo = fxVario.DB_TYPE_HYPERTEXT;
                return true;

            } else {
                this.tipo = fxVario.DB_TYPE_BINARY;
                return false;

            }
        } catch (NullPointerException ex) {
            //  System.out.println("\n Erro ao obter o tipo de ficheiro " + ex.getMessage());
            return false;
        }

    }

    private int createDirectory(String dir) {
        File localDir = new File(dir);

        if (!localDir.exists()) {
            boolean success = (new File(dir)).mkdirs();
            if (!success) {
                return -2;
            }
            return 1;
        }
        return 1;
    }

    public void levelVerification(int nivelMax) {
        int charCount = this.fullWebPath.replaceAll("[^/]", "").length();
        this.nivel = (charCount - 2);
        if ((charCount - 2) <= nivelMax) {
            this.nivelMax = true;
        } else {
            this.nivelMax = false;
        }
    }

    private void verifyLastCharacter() {
        String lastCaract = this.localPath.substring(this.localPath.length() - 1);

        if (lastCaract.compareTo("/") != 0 && this.localPath.contains("/")) {
            this.localPath = this.localPath + "/";
        } else if (lastCaract.compareTo("\\") != 0 && this.localPath.contains("\\")) {
            this.localPath = this.localPath + "\\";
        }
    }

    private void URLVerifcation() {
        int tamanhoURL = this.fullWebPath.length();
        int posBarra = this.fullWebPath.lastIndexOf("/");
        posBarra++;
        String part = this.fullWebPath.substring(posBarra, tamanhoURL);
        if (posBarra <= 8) {
            this.fullWebPath = this.fullWebPath + "/";
            this.path = "/";
        } else if (part.contains(".")) {
            this.setFileName(testFileName(this.getFullWebPath()));
            setOnlyUrlPath();
            if (fileType()) {
                this.tipo = fxVario.DB_TYPE_HYPERTEXT;
            } else {
                this.tipo = fxVario.DB_TYPE_BINARY;
            }
        } else {

//            verificar quando não enviar o index.html

            this.fileName = "index.html";
            setOnlyUrlPath();
        }
    }

    public boolean isPagetoParser() {
        return this.fileType();
    }

    private String convertSlash(String str) {
        String tmp = null;
        if (str.contains("/") && this.isWindows()) {
            tmp = str.replace("/", "\\");
        } else {
            tmp = str;
        }
        return tmp;
    }

    private boolean isWindows() {
        return this.windows;
    }

    private void ifOnlyHost() {
        int t = 0;
        if (this.getProtocol().length() == 4) {
            t = 7; //caso http://
        } else {
            t = 8; //caso https://
        }

        t += this.getHost().length();
        t += this.getPath().length();

        int y = socof_webcrawler.SOCOF_WebCrawler.getDOMAIN().length() + 1;
        if (t == (this.getFullWebPath().length()) && t <= y) {
            this.setFileName("index.html");

            String lastCaract = this.getFullWebPath().substring(this.getFullWebPath().length() - 1);
            if (lastCaract.compareTo("/") != 0) {
                this.setFullWebPath(this.getFullWebPath() + "/");
                this.path += "/";
            }
            this.tipo = fxVario.DB_TYPE_HYPERTEXT;
        }
    }

    /*
     * //verifica e atualiza o tamanho caso seja diferente
     */
    public void localFileLength() {
        File file = new File(this.getFullLocalPathFile());

        long byteSize = file.length();

        // Divide by 1024 to get size in KB
        long kbSize = byteSize / 1024;

        //     System.out.println(byteSize + " bytes");
        if (this.size != byteSize) {
            this.size = byteSize;
        }
    }

    //GETTERS AND SETTERS
    public int getNivel() {
        return nivel;
    }

    /*
     * set do fullWebPath
     */
    private void setFullWebPath(String webPath) {

        this.fullWebPath = webPath;
    }

    /*
     * define o host
     */
    private void setHost() {
        this.host = this.url.getHost();
    }

    private void setSize() {
        this.size = this.getFileSize();
    }

    /*
     * define o path local
     */
    private void setLocalPath(String localPath) {
        this.localPath = localPath;
    }

    private void setProtocol() {
        this.protocol = this.url.getProtocol();
    }

    private String getProtocol() {
        return this.protocol;
    }

    private int getFileSize() {
        HttpURLConnection conn = null;
        try {
            conn = (HttpURLConnection) this.url.openConnection();
            conn.setRequestMethod("HEAD");
            conn.getInputStream();

            return conn.getContentLength();

        } catch (IOException e) {
            System.out.println("\n Erro ao obter o tamanho do ficheiro " + e.getMessage());
            return -1;
        } finally {
            conn.disconnect();
        }
    }

    private void setWindows() {
        if (this.localPath.contains("\\")) {
            this.windows = true;
        } else {
            this.windows = false;
        }

    }

    private void setOnlyUrlPath() {
        try {
            this.path = this.url.getPath().replaceAll(this.fileName, "");
        } catch (PatternSyntaxException e) {
            this.path = this.url.getPath();
        }
    }

    private String getLocalPath() {
        return this.localPath;
    }

    private void setFileName(String fileName) {
        this.fileName = fileName;
    }

    private static int getResponseCode(String urlString) throws MalformedURLException, IOException {
        URL u = new URL(urlString);
        HttpURLConnection huc = (HttpURLConnection) u.openConnection();
        huc.setRequestMethod("GET");
        huc.connect();
        return huc.getResponseCode();
    }

    /*
     * retorna o path na web do url passado
     */
    public String getFullWebPath() {
        return this.fullWebPath;
    }

    /*
     * retorna o tamanho do ficheiro do url em bytes (int)
     * no caso de endereço errado ou inexistente retorna -1
     */
    public long getSize() {
        return size;
    }

    //retorna full path local
    public String getFullLocalPath() {
        String tmp = this.getLocalPath();
        if (tmp == null || tmp.toLowerCase().equals("null")) {
            tmp = socof_webcrawler.SOCOF_WebCrawler.getPATH() + "\\";
        }
        if (this.getHost() != null) {
            tmp += this.getHost();
        } else {
            tmp = socof_webcrawler.SOCOF_WebCrawler.getDOMAIN() + "\\";
        }
        if (!this.getPath().contains("?")) {
            tmp += this.getPath();
        } else {
            tmp += this.getPath().split("\\?")[0];
        }
        return tmp;
    }

    //retorna full path com file
    public String getFullLocalPathFile() {
        if (getFullLocalPath().toLowerCase().contains(this.getFileName())) {
            return getFullLocalPath();
        } else {
            if (this.getFileName().contains("?")) {
                String tmp = this.getFileName().split("\\?")[0];
                this.setFileName(tmp);
            }
            String filePpath;
            if (!this.getFullLocalPath().toLowerCase().contains(this.getFileName().toLowerCase())) {
                filePpath = getFullLocalPath() + this.getFileName();
            } else {
                filePpath = getFullLocalPath();
            }
            return filePpath;
        }
    }

    /*
     * retorna o host/site d url
     */
    public String getHost() {

        return this.host;
    }

    /*
     * retorna o nome do ficheiro
     */
    public String getFileName() {
        return fileName;
    }

    /*
     * retorna o path
     */
    public String getPath() {
        return path;
    }
}
